/*
 * Copyright (c) 2012 Tomas Shestakov. <http://code.google.com/p/jmmo/>
 */

package jmmo.engine.property

/**
 * Observable value that can be changed and can has listeners.
 * Properties can be used instead of any vars. For example:<br>
 * `val p = Property(0)` declare Property[Int]<br>
 * `p() = 2` assign new value to the Property<br>
 * `println(p())` prints the Property value.<br>
 * </code>
 * If you change property value its listeners will be informed by [[jmmo.engine.property.ChangePropertyEvent]]
 * <p>
 * The Observer is the class of changes which can be observed.
 * </p>
 * @tparam A type of `Property` value
 * @author Tomas Shestakov
 */
trait Property[A] extends AlterableObservableValue[A] with Equals {

  /**
   * Mark what Property value can be updated.
   * if returns false update method cause [[java.lang.UnsupportedOperationException]].
   * @return true if `Property` value can be updated.
   */
  def isWritable: Boolean

  /**
   * Mark what Property value can be read.
   * if returns false apply method cause [[java.lang.UnsupportedOperationException]].
   * @return true if `Property` value can be read.
   */
  def isReadable: Boolean

  /**
   * Change `Property` value to specified one.
   * @param value new Property value
   */
  def update(value: A)
}

/**
 * Contains factory methods to create instances derived from [[jmmo.engine.property.Property]].
 * Currently creates instances of class [[jmmo.engine.property.RegularProperty]].
 * Extractor method `unapply` extract only Property `id` and `value` without `attributes`.
 */
object Property {

  /**
   * Creates a `Property` with specified initial value, id and attributes.
   * @param init initial value of `Property`
   * @param id unique identifier of `Property`
   * @tparam A type of `Property` value
   */
  def apply[A](init: A = jmmo.util.sysDefault, id: Identifier = Identifier.empty) = new RegularProperty[A](init, id)

  /**
   * This extractor method is called in a pattern match { case Property(...) => }.
   * @param property
   * @tparam A `type` of Property value
   * @return Property `id` and `value` wrapped in an Option.
   */
  def unapply[A](property: Property[A]): Option[(A, Identifier)] =
    if (property eq null) None
    else Some(property(), property.id)
}
